﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Wrapper.Direct3D;
using Vortex.Wrapper;
using Vortex.Debugging;

namespace Vortex.Drawing {

	/// <summary>
	/// Implements common part for all of target textures
	/// </summary>
	public abstract class BaseTargetTexture : BaseTexture, IDrawingTarget {
		protected Canvas2D _canvas;
		protected Direct3DSurface9 _zBuffer;
		protected Direct3DSurface9 _sharedZBuffer;
		protected bool _createZSurface;

		/// <summary>
		/// Initializes a new instance of the <see cref="BaseTargetTexture"/> class.
		/// </summary>
		protected BaseTargetTexture() {
			_canvas = new Canvas2D(this, Device.Renderer);
		}

		#region Maintain methods

		/// <summary>
		/// Invoked before the device is reset to all device surface references.
		/// </summary>
		protected override void BeforeDeviceReset() {
			//just free data
			FreeData();
		}

		/// <summary>
		/// Invoked after the device is reset successfully to restore everything.
		/// </summary>
		protected override void AfterDeviceReset() {
			InitializeTargetTexture(Size.Width, Size.Height, Format, _createZSurface);
		}

		#endregion

		/// <summary>
		/// Frees the internal data of target texture.
		/// </summary>
		protected override void FreeData() {
			if (null != _zBuffer) {
				_zBuffer.Dispose();
				_zBuffer = null;
			}
			//release everything else
			base.FreeData();

			//call event
			if (null != OnDestroySurface) {
				OnDestroySurface(this);
			}

		}

		/// <summary>
		/// Initializes the target texture with specified parameters.
		/// </summary>
		/// <param name="width">The width of target.</param>
		/// <param name="height">The height of target.</param>
		/// <param name="format">The format of target.</param>
		protected void InitializeTargetTexture(int width, int height, PixelFormat format, bool createZSurface) {
			Direct3DTexture9 texture;

			ValidateSize(width, height);

			//step1, create texture
			HResult result = Device.ApiRef.CreateTexture(
				(uint)width,
				(uint)height,
				1,
				D3DUsage.RenderTarget,
				(D3DFormat)format,
				D3DPool.Default,
				out texture
			);
			Watcher.AssertCritical(result, "Can't create render target texture with parameters {0}x{1}:{2}", width, height, format);
			Initialize(texture);

			//step2, probably create Z-Buffer
			_createZSurface = createZSurface;
			if (createZSurface) {
				result = Device.ApiRef.CreateDepthStencilSurface((uint)width, (uint)height, D3DFormat.D16, D3DMultisampleType.None, 0, true, out _zBuffer);
				Watcher.AssertCritical(result, "Can't create depth stencil surface for render targetwith parameters {0}x{1}:{2}", width, height, format);
			}

			//call event
			if (null != OnCreateSurface) {
				OnCreateSurface(this);
			}
		}

		/// <summary>
		/// Invokes the surface resize event.
		/// </summary>
		protected void InvokeSurfaceResizeEvent() {

			if (null != OnResizeSurface) {
				OnResizeSurface(this);
			}
		}

		#region IDrawingTarget Members
	

		/// <summary>
		/// Gets the instance of canvas to draw on this texture.
		/// </summary>
		/// <value>The canvas instance.</value>
		public Canvas2D Canvas {
			get { return _canvas; }
		}

		/// <summary>
		/// Gets a value indicating whether drawing target has Z buffer and supports Z-Effects.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this target has Z buffer; otherwise, <c>false</c>.
		/// </value>
		public bool HasZBuffer {
			get { return _zBuffer != null || _sharedZBuffer != null; }
		}

		/// <summary>
		/// Gets the drawing target internal data.
		/// </summary>
		/// <value>The drawing target data.</value>
		public TargetData TargetData {
			get { return new TargetData(_surface.ApiRef, _zBuffer ?? _sharedZBuffer); }
		}


		/// <summary>
		/// Occurs when target texture surface is created.
		/// </summary>
		public event DrawingTargetEventHandler OnCreateSurface;

		/// <summary>
		/// Occurs when target texture surface is destroyed.
		/// </summary>
		public event DrawingTargetEventHandler OnDestroySurface;

		/// <summary>
		/// Occurs when drawing target was resized.
		/// </summary>
		public event DrawingTargetEventHandler OnResizeSurface;

		#endregion
	}
}
